# -*- coding: utf-8 -*-
# Copyright under  the latest Apache License 2.0
from cgi import parse_qs, parse_qsl
from hmac import new as hmac
from random import getrandbits
from time import time
from urllib import urlencode, quote as urlquote, unquote as urlunquote
import urlparse, logging, base64
#------------------------------------------------------------------------------ 
import sha as sha1
#Django imports
import models
import datetime
from django.core.cache import cache

#Syslib imports
import urllib2
from urllib2 import Request

import Config

#===============================================================================
# Changed hashlib import from 2.5 to 2.7 
#===============================================================================
# the block size for the cipher object; must be 16, 24, or 32 for AES
BLOCK_SIZE = 32

# the character used for padding--with a block cipher such as AES, the value
# you encrypt must be a multiple of BLOCK_SIZE in length.  This character is
# used to ensure that your value is always a multiple of BLOCK_SIZE
PADDING = '{'

# one-liner to sufficiently pad the text to be encrypted
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING

# one-liners to encrypt/encode and decrypt/decode a string
# encrypt with AES, encode with base64
EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)

if Config.ENABLE_LOGGING:
    oauthLogger = logging.getLogger('gtap.oauth')
    oauthLogger.setLevel(Config.LOGGING_LEVEL)
    handler = logging.FileHandler(Config.LOGGING_PATH, 'a')
    formatter = logging.Formatter(Config.LOGGING_FORMAT)
    handler.setFormatter(formatter)
    oauthLogger.addHandler(handler)

class OAuthException(Exception):
    pass

class OAuthClient():
    
    
    def __init__(self, service_name, consumer_key, consumer_secret, request_url,
               access_url, callback_url=None):
        """ Constructor."""

        self.service_name = service_name
        self.consumer_key = consumer_key
        self.consumer_secret = consumer_secret
        self.request_url = request_url
        self.access_url = access_url
        self.callback_url = callback_url

    def prepare_request(self, url, token="", secret="", additional_params=None,
                      method='GET'):
        oauthLogger.debug("prepare_request()")
        """Prepare Request.

        Prepares an authenticated request to any OAuth protected resource.

        Returns the payload of the request.
        """

        def encode(text):
            return urlquote(str(text), "")

        params = {
            "oauth_consumer_key": self.consumer_key,
            "oauth_signature_method": "HMAC-SHA1",
            "oauth_timestamp": str(int(time())),
            "oauth_nonce": str(getrandbits(64)),
            "oauth_version": "1.0"
        }

        if token:
            params["oauth_token"] = token
        elif self.callback_url:
            params["oauth_callback"] = self.callback_url

        if additional_params:
            params.update(additional_params)

        for k, v in params.items():
            if isinstance(v, unicode):
                params[k] = v.encode('utf8')
            if type(v) is str:
                params[k] = params[k].replace("~", "~~~")
            
        # Join all of the params together.
        params_str = "&".join(["%s=%s" % (encode(k), encode(params[k]))
                               for k in sorted(params)])

        # Join the entire message together per the OAuth specification.
        message = "&".join(['GET' if method == 'GET' else "POST",
                            encode(url), encode(params_str)])

        # Create a HMAC-SHA1 signature of the message.
        key = "%s&%s" % (self.consumer_secret, secret) # Note compulsory "&".
        message = message.replace('%257E%257E%257E', '~')
        signature = hmac(key, message, sha1)
        digest_base64 = signature.digest().encode("base64").strip()
        params["oauth_signature"] = digest_base64

        # Construct the request payload and return it
        return urlencode(params).replace('%7E%7E%7E', '~')
    
    def make_request(self, url, token="", secret="", additional_params=None,
                                      protected=False, method='GET'):
        oauthLogger.debug("make_request()")
        
        (scm, netloc, path, params, query, _) = urlparse.urlparse(url)
        url = None
        query_params = None
        if query:
            query_params = dict([(k, v) for k, v in parse_qsl(query)])
            additional_params.update(query_params)
        url = urlparse.urlunparse(('https', netloc, path, params, '', ''))
        
        #Oauth steps: generated correct auth url
        payload = self.prepare_request(url, token, secret, additional_params, method)

        request = None
        if method == 'GET': 
            url = "%s?%s" % (url, payload)
            payload = None
            request = Request(url)
        else:
            request = Request(url)
            request.add_data(payload)
                
        
        request.add_header('Authorization', "OAuth") if protected else {}
        
        # need more discover on post request
        
        response = urllib2.urlopen(request, None, 100)
        
        msg = response.read()
        response.close()
        
        return msg
        
    def get_access_token(self, auth_token, auth_verifier):
        oauthLogger.debug("get_access_token()")

        auth_token = urlunquote(auth_token)
        auth_verifier = urlunquote(auth_verifier)

        response = self.make_request(self.access_url,
                                token=auth_token,
                                additional_params={"oauth_verifier": auth_verifier}
                                )

        # Extract the access token/secret from the response.
        result = self._extract_credentials(response)
        return result['token'], result['secret'], result['screen_name']
      
    def get_access_from_db(self, username, password):
        oauthLogger.debug("get_access_from_db()")
        access_token = cache.get(username + '_access_token')
        access_secret = cache.get(username + '_access_secret')
        if access_token is not None and access_secret is not None:
            return access_token, access_secret
       
        else:
            
            res = models.TwitterUser.objects.filter(userName=username.lower())
            resList = list(res)
            if not res:
                access_token = None
                access_secret = None
            else:
                user = resList[0]
                user.decrypt(password)
                
                if user.oauthToken[:3] == '###' and user.oauthSecret[:3] == '###':
                    access_token = user.oauthToken[3:]
                    access_secret = user.oauthSecret[3:]
                    cache.set(username + '_access_token' , access_token)
                    cache.set(username + '_access_secret' , access_secret)
                else:
                    access_token = None
                    access_secret = None
            return access_token, access_secret

    def save_user_info_into_db(self, username, password, token, secret):
        oauthLogger.debug("save_user_info_into_db()")
        
        res = models.TwitterUser.objects.filter(userName=username.lower())
        if res.count() > 0:
            res.delete()

        token = '###' + token
        secret = '###' + secret

        user = models.TwitterUser(oauthService=self.service_name,
                         userName=username.lower(),
                         oauthSecret=secret,
                         oauthToken=token)
        user.encrypt(password)
        user.dateCreated = datetime.datetime.now()
        user.save()
    
    def _get_auth_token(self):
        oauthLogger.debug("_get_auth_token()")
        """Get Authorization Token.

        Actually gets the authorization token and secret from the service. The
        token and secret are stored in our database, and the auth token is
        returned.
        """
        response_url = self.make_request(self.request_url)
        result = self._extract_credentials(response_url)

        auth_token = result["token"]
        auth_secret = result["secret"]

        return auth_token

    def _extract_credentials(self, result):
        oauthLogger.debug("_extract_credentials()")
        
        """Extract Credentials.

        Returns an dictionary containing the token and secret (if present).
        Throws an Exception otherwise.
        """
        token = None
        secret = None
        screen_name = None
        parsed_results = parse_qs(result)
        
        #parsed_results = parse_qs(content)
        
        if "oauth_token" in parsed_results:
            token = parsed_results["oauth_token"][0]

        if "oauth_token_secret" in parsed_results:
            secret = parsed_results["oauth_token_secret"][0]

        if "screen_name" in parsed_results:
            screen_name = parsed_results["screen_name"][0]

        if not (token and secret) or result is None:
            logging.error("Response Status Code is : %s" % result.status_code)
            logging.error("Could not extract token/secret: %s" % result.content)
            raise OAuthException("Problem talking to the service")

        return {
            "service": self.service_name,
            "token": token,
            "secret": secret,
            "screen_name": screen_name
        }
    
class TwitterClient(OAuthClient):

    def __init__(self, consumer_key, consumer_secret, callback_url):
        """Constructor."""

        OAuthClient.__init__(self,
                "twitter",
                consumer_key,
                consumer_secret,
                "https://api.twitter.com/oauth/request_token",
                "https://api.twitter.com/oauth/access_token",
                callback_url)

    def get_authorization_url(self):
        oauthLogger.debug("get_authorization_url()")


        """Get Authorization URL."""

        token = self._get_auth_token()
        return "https://api.twitter.com/oauth/authorize?oauth_token=%s" % token

